#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
线程安全的符号管理器
解决并发处理时的竞态条件问题
"""

import threading
import time
from typing import Dict, Set, Optional, List, Any, Callable
from collections import defaultdict
from dataclasses import dataclass, field
from datetime import datetime

from ..utils_module.logger import ObfuscationLogger


@dataclass
class SymbolConflict:
    """符号冲突记录"""
    symbol: str
    existing_name: str
    new_name: str
    thread_id: int
    timestamp: datetime
    resolution: str = ""  # resolved/ignored/pending


@dataclass
class ThreadStatistics:
    """线程统计信息"""
    thread_id: int
    symbols_processed: int = 0
    conflicts_encountered: int = 0
    retries: int = 0
    processing_time: float = 0.0


class ThreadSafeSymbolManager:
    """线程安全的符号管理器"""

    def __init__(self, logger: ObfuscationLogger = None):
        """
        初始化线程安全符号管理器

        Args:
            logger: 日志记录器
        """
        self.logger = logger

        # 符号映射表（原始符号 -> 混淆后符号）
        self.symbol_map: Dict[str, str] = {}
        self.symbol_map_lock = threading.RLock()

        # 已使用的混淆名称集合
        self.used_names: Set[str] = set()
        self.used_names_lock = threading.RLock()

        # 文件级锁，防止同时修改同一文件
        self.file_locks: Dict[str, threading.RLock] = defaultdict(threading.RLock)
        self.file_locks_lock = threading.Lock()

        # 符号生成计数器（用于唯一性保证）
        self.generation_counter = 0
        self.counter_lock = threading.Lock()

        # 冲突记录
        self.conflicts: List[SymbolConflict] = []
        self.conflicts_lock = threading.Lock()

        # 线程统计
        self.thread_stats: Dict[int, ThreadStatistics] = {}
        self.stats_lock = threading.Lock()

        # 预留符号（系统符号等）
        self.reserved_symbols: Set[str] = set()
        self.reserved_lock = threading.RLock()

        if logger:
            logger.log_operation("符号管理器", "线程安全符号管理器已初始化")

    def generate_obfuscated_name_safe(self, original: str,
                                     generator_func: Callable[[str, int], str],
                                     max_attempts: int = 1000) -> str:
        """
        线程安全的混淆名称生成

        Args:
            original: 原始符号名
            generator_func: 名称生成函数
            max_attempts: 最大尝试次数

        Returns:
            唯一的混淆名称

        Raises:
            RuntimeError: 无法生成唯一名称
        """
        thread_id = threading.current_thread().ident
        self._update_thread_stats(thread_id, 'symbols_processed')

        # 先检查是否已有映射
        with self.symbol_map_lock:
            if original in self.symbol_map:
                return self.symbol_map[original]

        # 生成新名称
        for attempt in range(max_attempts):
            # 使用计数器确保唯一性
            with self.counter_lock:
                counter = self.generation_counter
                self.generation_counter += 1

            candidate = generator_func(original, counter)

            # 原子操作：检查并添加
            with self.used_names_lock:
                if candidate not in self.used_names and not self._is_reserved(candidate):
                    # 立即添加到已使用集合
                    self.used_names.add(candidate)

                    # 更新符号映射（双重检查锁定模式）
                    with self.symbol_map_lock:
                        if original in self.symbol_map:
                            # 其他线程已经生成了映射
                            with self.used_names_lock:
                                self.used_names.discard(candidate)  # 释放候选名称
                            return self.symbol_map[original]
                        else:
                            # 成功添加映射
                            self.symbol_map[original] = candidate
                            if self.logger:
                                self.logger.log_operation("符号映射",
                                                        f"{original} -> {candidate}")
                            return candidate

            # 记录重试
            if attempt > 0:
                self._update_thread_stats(thread_id, 'retries')

        # 所有尝试都失败了
        error_msg = f"无法为 {original} 生成唯一名称（尝试了 {max_attempts} 次）"
        if self.logger:
            self.logger.log_error(error_msg)
        raise RuntimeError(error_msg)

    def update_symbol_map_safe(self, symbol: str, obfuscated: str) -> bool:
        """
        线程安全的符号映射更新

        Args:
            symbol: 原始符号
            obfuscated: 混淆后的名称

        Returns:
            是否成功更新
        """
        thread_id = threading.current_thread().ident

        with self.symbol_map_lock:
            if symbol in self.symbol_map:
                existing = self.symbol_map[symbol]
                if existing != obfuscated:
                    # 记录冲突
                    self._record_conflict(symbol, existing, obfuscated, thread_id)
                    return False
                else:
                    # 已存在相同映射
                    return True

            # 检查名称是否可用
            with self.used_names_lock:
                if obfuscated in self.used_names:
                    # 名称已被使用，记录冲突
                    self._record_conflict(symbol, "N/A", obfuscated, thread_id)
                    return False

                # 添加映射
                self.symbol_map[symbol] = obfuscated
                self.used_names.add(obfuscated)

                if self.logger:
                    self.logger.log_operation("符号更新", f"{symbol} -> {obfuscated}")

                return True

    def get_obfuscated_name(self, original: str) -> Optional[str]:
        """
        线程安全地获取混淆名称

        Args:
            original: 原始符号

        Returns:
            混淆后的名称，如果不存在则返回None
        """
        with self.symbol_map_lock:
            return self.symbol_map.get(original)

    def acquire_file_lock(self, file_path: str) -> threading.RLock:
        """
        获取文件锁

        Args:
            file_path: 文件路径

        Returns:
            文件锁对象
        """
        with self.file_locks_lock:
            if file_path not in self.file_locks:
                self.file_locks[file_path] = threading.RLock()
            return self.file_locks[file_path]

    def batch_update_symbols(self, symbol_mappings: Dict[str, str]) -> Dict[str, bool]:
        """
        批量更新符号（原子操作）

        Args:
            symbol_mappings: 符号映射字典

        Returns:
            更新结果 {符号: 是否成功}
        """
        results = {}
        thread_id = threading.current_thread().ident

        # 先检查所有名称的可用性
        with self.symbol_map_lock:
            with self.used_names_lock:
                # 检查冲突
                conflicts = []
                for symbol, obfuscated in symbol_mappings.items():
                    if symbol in self.symbol_map:
                        if self.symbol_map[symbol] != obfuscated:
                            conflicts.append((symbol, self.symbol_map[symbol], obfuscated))
                    elif obfuscated in self.used_names:
                        conflicts.append((symbol, "N/A", obfuscated))

                if conflicts:
                    # 有冲突，记录并返回失败
                    for symbol, existing, new in conflicts:
                        self._record_conflict(symbol, existing, new, thread_id)
                        results[symbol] = False
                    return results

                # 无冲突，批量更新
                for symbol, obfuscated in symbol_mappings.items():
                    if symbol not in self.symbol_map:
                        self.symbol_map[symbol] = obfuscated
                        self.used_names.add(obfuscated)
                        results[symbol] = True
                    else:
                        results[symbol] = True  # 已存在相同映射

        if self.logger:
            success_count = sum(1 for v in results.values() if v)
            self.logger.log_operation("批量更新",
                                    f"成功: {success_count}/{len(symbol_mappings)}")

        return results

    def add_reserved_symbols(self, symbols: Set[str]):
        """
        添加保留符号（不能用作混淆名称）

        Args:
            symbols: 保留符号集合
        """
        with self.reserved_lock:
            self.reserved_symbols.update(symbols)

    def _is_reserved(self, name: str) -> bool:
        """检查名称是否为保留符号"""
        with self.reserved_lock:
            return name in self.reserved_symbols

    def _record_conflict(self, symbol: str, existing: str, new: str, thread_id: int):
        """
        记录冲突

        Args:
            symbol: 原始符号
            existing: 现有映射
            new: 新的映射尝试
            thread_id: 线程ID
        """
        conflict = SymbolConflict(
            symbol=symbol,
            existing_name=existing,
            new_name=new,
            thread_id=thread_id,
            timestamp=datetime.now(),
            resolution="pending"
        )

        with self.conflicts_lock:
            self.conflicts.append(conflict)

        self._update_thread_stats(thread_id, 'conflicts_encountered')

        if self.logger:
            self.logger.log_warning(
                f"符号冲突: {symbol} - 现有: {existing}, 尝试: {new} (线程: {thread_id})"
            )

    def _update_thread_stats(self, thread_id: int, stat_type: str):
        """更新线程统计信息"""
        with self.stats_lock:
            if thread_id not in self.thread_stats:
                self.thread_stats[thread_id] = ThreadStatistics(thread_id=thread_id)

            stats = self.thread_stats[thread_id]
            if stat_type == 'symbols_processed':
                stats.symbols_processed += 1
            elif stat_type == 'conflicts_encountered':
                stats.conflicts_encountered += 1
            elif stat_type == 'retries':
                stats.retries += 1

    def get_statistics(self) -> Dict[str, Any]:
        """
        获取统计信息

        Returns:
            统计信息字典
        """
        with self.symbol_map_lock:
            symbol_count = len(self.symbol_map)

        with self.used_names_lock:
            name_count = len(self.used_names)

        with self.conflicts_lock:
            conflict_count = len(self.conflicts)
            pending_conflicts = sum(1 for c in self.conflicts if c.resolution == "pending")

        with self.stats_lock:
            thread_stats = {
                'total_threads': len(self.thread_stats),
                'total_symbols_processed': sum(s.symbols_processed for s in self.thread_stats.values()),
                'total_conflicts': sum(s.conflicts_encountered for s in self.thread_stats.values()),
                'total_retries': sum(s.retries for s in self.thread_stats.values())
            }

        return {
            'symbols_mapped': symbol_count,
            'names_generated': name_count,
            'total_conflicts': conflict_count,
            'pending_conflicts': pending_conflicts,
            'file_locks': len(self.file_locks),
            'reserved_symbols': len(self.reserved_symbols),
            **thread_stats
        }

    def resolve_conflicts(self, resolution_strategy: str = 'keep_first') -> int:
        """
        解决冲突

        Args:
            resolution_strategy: 解决策略
                - 'keep_first': 保留第一个映射
                - 'keep_last': 使用最后的映射
                - 'regenerate': 重新生成

        Returns:
            解决的冲突数量
        """
        resolved = 0

        with self.conflicts_lock:
            for conflict in self.conflicts:
                if conflict.resolution == "pending":
                    if resolution_strategy == 'keep_first':
                        # 保留现有映射
                        conflict.resolution = "resolved"
                        resolved += 1
                    elif resolution_strategy == 'keep_last':
                        # 更新为新映射
                        with self.symbol_map_lock:
                            with self.used_names_lock:
                                old_name = self.symbol_map.get(conflict.symbol)
                                if old_name:
                                    self.used_names.discard(old_name)
                                self.symbol_map[conflict.symbol] = conflict.new_name
                                self.used_names.add(conflict.new_name)
                        conflict.resolution = "resolved"
                        resolved += 1
                    elif resolution_strategy == 'regenerate':
                        # 标记为需要重新生成
                        conflict.resolution = "regenerate"
                        resolved += 1

        if self.logger:
            self.logger.log_operation("冲突解决",
                                    f"解决了 {resolved} 个冲突（策略: {resolution_strategy}）")

        return resolved

    def clear(self):
        """清空所有数据（用于重置）"""
        with self.symbol_map_lock:
            self.symbol_map.clear()

        with self.used_names_lock:
            self.used_names.clear()

        with self.conflicts_lock:
            self.conflicts.clear()

        with self.stats_lock:
            self.thread_stats.clear()

        self.file_locks.clear()

        with self.counter_lock:
            self.generation_counter = 0

        if self.logger:
            self.logger.log_operation("符号管理器", "已清空所有数据")

    def export_mapping(self) -> Dict[str, str]:
        """
        导出符号映射（线程安全）

        Returns:
            符号映射的副本
        """
        with self.symbol_map_lock:
            return self.symbol_map.copy()

    def import_mapping(self, mapping: Dict[str, str]):
        """
        导入符号映射（线程安全）

        Args:
            mapping: 要导入的符号映射
        """
        with self.symbol_map_lock:
            with self.used_names_lock:
                for symbol, obfuscated in mapping.items():
                    if symbol not in self.symbol_map:
                        self.symbol_map[symbol] = obfuscated
                        self.used_names.add(obfuscated)

        if self.logger:
            self.logger.log_operation("符号导入", f"导入了 {len(mapping)} 个符号映射")